package hr.sting.droidorm;

import hr.sting.beans.BeanUtils;
import hr.sting.beans.PropertyInfo;
import hr.sting.droidorm.util.StringUtils;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.PersistenceException;
import java.util.*;

/**
 * @author mstipanov
 * @since 10.07.2010. 17:20:25
 */
public class OrmEntityDescriptor<T> {
    private Map<String, PropertyInfo> propertyMap;
    private Class<T> entityType;
    private String tableName;
    private String[] columnNames;
    private PropertyInfo primaryKey;

    public OrmEntityDescriptor(Class<T> entityType) {
        this.entityType = entityType;
    }

    public PropertyInfo[] getProperties() {
        Map<String, PropertyInfo> propertyMap = getPropertyMap();
        Collection<PropertyInfo> properties = propertyMap.values();
        return properties.toArray(new PropertyInfo[properties.size()]);
    }

    private Map<String, PropertyInfo> getPropertyMap() {
        if (null != propertyMap) {
            return propertyMap;
        }

        propertyMap = new HashMap<String, PropertyInfo>();
        PropertyInfo[] properties = BeanUtils.getProperties(entityType);
        for (PropertyInfo property : properties) {
            propertyMap.put(getColumnName(property).toLowerCase(), property);
        }
        return propertyMap;
    }

    public Class<T> getEntityType() {
        return entityType;
    }

    public String[] getColumnNames() {
        if (null != columnNames) {
            return columnNames;
        }

        List<String> columnNames = new LinkedList<String>();

        for (PropertyInfo propertyInfo : getProperties()) {
            if (propertyInfo.isWrittable()) {
                columnNames.add(getColumnName(propertyInfo));
            }
        }

        this.columnNames = columnNames.toArray(new String[columnNames.size()]);
        return this.columnNames;
    }

    private String getColumnName(PropertyInfo propertyInfo) {
        Column column = propertyInfo.getAnnotation(Column.class);
        if (null != column && !StringUtils.isNullOrEmpty(column.name())) {
            return column.name();
        }

        return propertyInfo.getName();
    }

    public String getTableName() {
        if (null != tableName) {
            return tableName;
        }

        Entity entity = entityType.getAnnotation(Entity.class);
        if (null == entity) {
            throw new PersistenceException(entityType.getName() + " must have an javax.persistence.Entity annotation!");
        }

        tableName = StringUtils.isNullOrEmpty(entity.name()) ? entityType.getSimpleName() : entity.name();
        return tableName;
    }

    public PropertyInfo getProperty(String columnName) {
        return getPropertyMap().get(columnName.toLowerCase());
    }

    public boolean isSaved(T t) {
        return null != getPrimaryKeyValue(t);
    }

    public boolean isPrimaryKey(String columnName) {
        return getPrimaryKeyColumnName().equalsIgnoreCase(columnName);
    }

    private PropertyInfo getPrimaryKey() {
        if (null != primaryKey) {
            return primaryKey;
        }
        for (PropertyInfo propertyInfo : getProperties()) {
            if (null != propertyInfo.getAnnotation(Id.class)) {
                primaryKey = propertyInfo;
                return primaryKey;
            }
        }
        throw new OrmException("Entity must have a Primary key!");
    }

    public String getPrimaryKeyColumnName() {
        PropertyInfo primaryKey = getPrimaryKey();
        for (String columnName : propertyMap.keySet()) {
            if (propertyMap.get(columnName).equals(primaryKey))
                return columnName;
        }
        throw new OrmException("Entity must have a Primary key!");
    }

    public String getPrimaryKeyValue(T t) {
        Object value = getPrimaryKey().getValue(t);
        String s = value.toString();
        if (null == value || ((value instanceof Number) && s == "0")) {
            return null;
        }
        return s;
    }
}
